#include <iostream>
#include <cmath>
#include "Physical/generator.h"

//
// Constructors/Destructors
//

/**
 * Empty Constructor
 */
physical::generator::generator ( )
{
    initAttributes();
}

/**
 * Constructor
 * @param  production_curve List of production curve constants from equ. form:
 * [1]+[2]*P^2+[3]*P^3+[4]*P^4+[5]*P^5+...
 * @param  min_pwr minimum operational output power of generator.
 * @param  max_pwr maximum operational output power of generator.
 * @param  min_up_time minimum operatin time of generator.
 * @param  min_down_time minimum resting time of generator before started again.
 * @param  startup_cost monetary value to start the generator.
 */
physical::generator::generator (int order, float* production_curve, float min_pwr, float max_pwr,
                      float min_up_time, float min_down_time, float startup_cost)
{
    initAttributes();
    numProdConst = order+1;
    // TEMP
    //std::cout << "numProdConst{" << numProdConst << "}" << std::endl;
    if (production_curve != NULL)
    {
        prodCurve = new float[numProdConst];
        prodIncCurve = new float[numProdConst];
        for (int i = 0; i<numProdConst; i++)
        {
            prodCurve[i] = production_curve[i];
            if (i > 0)
            {
                prodIncCurve[i-1] = i*production_curve[i];
            }
            // TEMP
            //std::cout << "prodCurve[" << i << "]{" << prodCurve[i] << "}" << std::endl;
        }
    }

    minPwr = min_pwr;
    maxPwr = max_pwr;
    minUpTime = min_up_time;
    minDownTime = min_down_time;
    startupCost = startup_cost;
    isOperating = false;
}

/**
 * Empty Destructor
 */
physical::generator::~generator ( )
{
    delete [] prodCurve;
    delete [] prodIncCurve;
}



//
// Methods
//
/**
 * Returns how much input it will take for the generator to meet the load.
 * @return double
 * @param  load
 */
double physical::generator::loadCost (float load )
{
    double sum = 0.00;
    if (prodCurve == NULL)
        return -1.00;
    else
    {
        int length = sizeof(prodCurve)/sizeof(*prodCurve);
        for (int i=0;i<length;i++)
        {
            sum += prodCurve[i]*pow(load,i);
        }
    }
    return sum;
}

/**
 * Changes the generator status and returns the set status. If current status is
 * "ON", then function begins shutdown sequence and returns FALSE (OFF). If current
 * status is "OFF", then function begins startup sequence and returns TRUE (ON).
 * @return bool
 */
bool physical::generator::switchGenStatus ( )
{
    if (getIsOperating())
    {
        setIsOperating(false);
        return false;
    }
    else if (!getIsOperating())
    {
        setIsOperating(true);
        return true;
    }
    else
        return true;
}

/**
 * Begins generator startup sequence and returns TRUE if commencement of startup is
 * successful, else returns FALSE. If generator is already running, returns TRUE.
 * @return bool
 */
bool physical::generator::setGenOn ( )
{
    setIsOperating(true);
    return true;
}

/**
 * Begins generator shutdown sequence and returns TRUE if commencement of shutdown
 * is successful, else returns FALSE. If generator is already resting, returns TRUE.
 * @return bool
 */
bool physical::generator::setGenOff ( )
{
    setIsOperating(false);
    return true;
}

/**
 * Schedule the generator to a certian power output.
 * Returns true if successful, else false (i.e. out of range)
 * @return bool
 * @param  load
 */
bool physical::generator::scheduleGen( double load )
{
    if ((load < getMinPwr())||(load > getMaxPwr()))
    {
        // TEMP
        //std::cout << "Requested output out of range." << std::endl;
        return false;
    }
    setElectoutput(load);
    if (getElectoutput() == load)
    {
        // TEMP
        //std::cout << "Generator set to " << getElectoutput() << std::endl;
        return true;
    }
    else
        return false;
}

/**
 * Returns the operating status of the generator.
 * @return bool
 */
bool physical::generator::getGenStat ( )
{
    return getIsOperating();
}

/**
 * Returns current output of the generator.
 * @return double
 */
double physical::generator::getGenOutput( )
{
    return getElectoutput();
}

/**
 * Returns minimum operating power output of the generator.
 */
float physical::generator::minPowerOutput( )
{
    return getMinPwr();
}

/**
 * Returns maximum operating power output of the generator.
 */
float physical::generator::maxPowerOutput( )
{
    return getMaxPwr();
}

/**
 * Return order of production curve equation.
 */
int physical::generator::getOrder( )
{
    return getNumProdConst()-1;
}

/**
 * Returns pointer to array of production curve constants
 */
float* physical::generator::getCurve( )
{
    return getProdCurve();
}

/**
 * Returns pointer to array of incremental production curve equation constants.
 * @return float pointer
 */
float* physical::generator::getIncrementalCurve( )
{
    return getProdIncCurve();
}

/**
 * This overloads the standard == operator for this specific class.
 */
bool physical::generator::operator== (const generator &rhs)
{
    if (this->electoutput != rhs.electoutput)
        return false;
    else if (this->isOperating != rhs.isOperating)
        return false;
    else if (this->startupCost != rhs.startupCost)
        return false;
    else if (this->minDownTime != rhs.minDownTime)
        return false;
    else if (this->minUpTime != rhs.minUpTime)
        return false;
    else if (this->maxPwr != rhs.maxPwr)
        return false;
    else if (this->minPwr != rhs.minPwr)
        return false;
    else if (this->numProdConst != rhs.numProdConst)
        return false;
    else
    {
        for (int i = 0; i < this->numProdConst; i++)
        {
            if (this->prodCurve != rhs.prodCurve)
                return false;
        }
    }
    return true;
}


//
// Accessor methods
//
/**
 * Set the value of numProdConst
 * @param new_var the new value of numProdConst
 */
void physical::generator::setNumProdConst ( int new_var )
{
    numProdConst = new_var;
}

/**
 * Get the value of numProdConst
 * @return the value of numProdConst
 */
int physical::generator::getNumProdConst ( )
{
    return numProdConst;
}

/**
 * Set the value of prodCurve
 * @param new_var the new value of prodCurve
 */
void physical::generator::setProdCurve ( float* new_var )
{
    prodCurve = new_var;
}

/**
 * Get the value of prodCurve
 * @return the value of prodCurve
 */
float* physical::generator::getProdCurve ( )
{
    return prodCurve;
}

/**
 * Set the value of prodIncCurve
 * @param new_var the new value of prodIncCurve
 */
void physical::generator::setProdIncCurve ( float* new_var )
{
    prodIncCurve = new_var;
}

/**
 * Get the value of prodIncCurve
 * @return the value of prodIncCurve
 */
float* physical::generator::getProdIncCurve ( )
{
    return prodIncCurve;
}

/**
 * Set the value of minPwr
 * Minimum power output for the generator.
 * @param new_var the new value of minPwr
 */
void physical::generator::setMinPwr ( float new_var )
{
    minPwr = new_var;
}

/**
 * Get the value of minPwr
 * Minimum power output for the generator.
 * @return the value of minPwr
 */
float physical::generator::getMinPwr ( )
{
    return minPwr;
}

/**
 * Set the value of maxPwr
 * Maximum power output of the generator.
 * @param new_var the new value of maxPwr
 */
void physical::generator::setMaxPwr ( float new_var )
{
    maxPwr = new_var;
}

/**
 * Get the value of maxPwr
 * Maximum power output of the generator.
 * @return the value of maxPwr
 */
float physical::generator::getMaxPwr ( )
{
    return maxPwr;
}

/**
 * Set the value of minUpTime
 * Minimum time running at operational conditions before the generator can be
 * shutdown.
 * @param new_var the new value of minUpTime
 */
void physical::generator::setMinUpTime ( float new_var )
{
    minUpTime = new_var;
}

/**
 * Get the value of minUpTime
 * Minimum time running at operational conditions before the generator can be shutdown.
 * @return the value of minUpTime
 */
float physical::generator::getMinUpTime ( )
{
    return minUpTime;
}

/**
 * Set the value of minDownTime
 * Minimum time after shutdown before the generator can begin startup sequence.
 * @param new_var the new value of minDownTime
 */
void physical::generator::setMinDownTime ( int new_var )
{
    minDownTime = new_var;
}

/**
 * Get the value of minDownTime
 * Minimum time after shutdown before the generator can begin startup sequence.
 * @return the value of minDownTime
 */
int physical::generator::getMinDownTime ( )
{
    return minDownTime;
}

/**
 * Set the value of startupCost
 * The monetary cost to bring the generator to operating conditions.
 * @param new_var the new value of startupCost
 */
void physical::generator::setStartupCost ( float new_var )
{
    startupCost = new_var;
}

/**
 * Get the value of startupCost
 * The monetary cost to bring the generator to operating conditions.
 * @return the value of startupCost
 */
float physical::generator::getStartupCost ( )
{
    return startupCost;
}

/**
 * Set the value of isOperating
 * The operating status of the generator: TRUE = outputing power, FALSE = not opperating
 * @param new_var the new value of isOperating
 */
void physical::generator::setIsOperating ( bool new_var )
{
    isOperating = new_var;
}

/**
 * Get the value of isOperating
 * The operating status of the generator: TRUE = outputing power, FALSE = not opperating
 * @return the value of isOperating
 */
bool physical::generator::getIsOperating ( )
{
    return isOperating;
}

/**
 * Set the value of electoutput
 * Current electrical output of generator.
 * @param new_var the new value of electoutput
 */
void physical::generator::setElectoutput ( double new_var )
{
    electoutput = new_var;
}

/**
 * Get the value of electoutput
 * Current electrical output of generator.
 * @return the value of electoutput
 */
double physical::generator::getElectoutput ( )
{
    return electoutput;
}


//
// Other methods
//

/**
 * Initialization function of class variables.
 */
void physical::generator::initAttributes ( ) {
    numProdConst = 0;
    prodCurve = NULL;
    minPwr = -1.00;
    maxPwr = -1.00;
    minUpTime = -1.00;
    minDownTime = -1.00;
    startupCost = -1.00;
    isOperating = false;
    electoutput = -1.00;
}
